/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sudokupso;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

/**
 *
 * @author Alex
 */
public class GeometricPSO implements Runnable {

    ArrayList<Particle> particleList = new ArrayList<Particle>();
    private int[][] fixedPositions;
    private Particle globalBest;
    private long globalBestFitness;
    private long[] iterationBest = new long[Constants.MAX_ITERATIONS];

    public GeometricPSO() {
    }

    public GeometricPSO(int[][] fixedPositions) {
        this.fixedPositions = fixedPositions;
    }

    public void run() {
        //for all particle i do
        for (int i = 0; i < Constants.MAX_PARTICLES; i++) {
            //initialise position xi at random in the search space
            particleList.add(new Particle(fixedPositions));
        }
        long maxFitness = Math.round(Math.sqrt(fixedPositions.length)) * fixedPositions.length * fixedPositions.length;
        //System.out.println("Max fitness = " + maxFitness);

        // initialize global best with first particle
        globalBest = (Particle) particleList.get(0).clone();
        globalBestFitness = globalBest.getFitness();
//        System.out.println("initial global best Fitness:" + globalBestFitness);
//        System.out.println("initial global best:");
//        System.out.println(globalBest);

        // initialize omega, phi1;
        double omega = Math.random();
        double phi1 = Math.random();

        int[] mask = new int[fixedPositions.length];
        //while stop criteria not met do
        int iterationsCounter = 0;
        while (iterationsCounter < Constants.MAX_ITERATIONS // max iterations reached
                && globalBestFitness < maxFitness) { // best possible fitness reached
            //System.out.println("current best fitness : " + globalBestFitness);
            //for all particle i do
            Iterator<Particle> iter = particleList.iterator();
            while (iter.hasNext()) {
                Particle p = iter.next();
                //set personal best ˆxi as best position found so far by the particle
                p.updateLocalBest();
                //set global best ˆg as best position found so far by the whole swarm
                if (p.getFitness() > globalBestFitness) {
                    globalBestFitness = p.getFitness();
                    globalBest = (Particle) p.clone();
                //System.out.println("----Global best evolution: " + globalBestFitness);
                //System.out.println(globalBest);
                }
            }
            //for all particle i do
            iter = particleList.iterator();

            while (iter.hasNext()) {
                Particle p = iter.next();
                generateMask(mask, omega, phi1);

                if (Math.random() < Constants.PMX_CROSSOVER_PROBABILITY) {
                    //update position using PMX
                    p.pmxCrossover(globalBest);
                } else {
                    //update position using a randomized convex combination
                    p.crossover(getGlobalBest(), mask);
                }
                //mutate xi with a certain probability
                if (Math.random() < Constants.MUTATION_PROBABILITY) {
                    p.mutate();
                }
            }
            omega = Math.random();
            phi1 = Math.random();
            iterationBest[iterationsCounter] = globalBestFitness;
            iterationsCounter++;
        }
    }

    /**
     * @param fixedPositions the fixedPositions to set
     */
    public void setFixedPositions(int[][] fixedPositions) {
        this.fixedPositions = fixedPositions;
    }

    /**
     * @return the globalBest
     */
    public Particle getGlobalBest() {
        return (Particle) globalBest.clone();
    }

    private void generateMask(int[] mask, double sociality, double memory) {
        // custom modifications here
        memory = 0.4; // memory
        sociality = 0.6; // sociality
        // end custom modifications

        int w1, w2, w3; // w1 - weight for particle, w2 - weight for localBest, w3 - weight for globalBest
        w3 = (int) (9 * sociality);
        w2 = (int) (9 * memory);
        w1 = 9 - w3 - w2;
        int value;
        for (int i = 0; i < mask.length; i++) {
            boolean valueAssigned = false;
            while (!valueAssigned) {
                value = (int) (1 + Math.random() * 3);
                switch (value) {
                    case 1:
                        w1--;
                        if (w1 >= 0) {
                            mask[i] = value;
                            valueAssigned = true;
                        }
                        break;
                    case 2:
                        w2--;
                        if (w2 >= 0) {
                            mask[i] = value;
                            valueAssigned = true;
                        }
                        break;
                    case 3:
                        w3--;
                        if (w3 >= 0) {
                            mask[i] = value;
                            valueAssigned = true;
                        }
                        break;
                    default:
                        System.out.println("Mask[i] != 1,2,3");
                        break;
                }
            }
        }
    }

    public long[] getIterationBest() {
        return iterationBest;
    }
}
